package cn.yto.print.plugins;

import cn.hutool.json.JSONUtil;
import cn.yto.print.config.i18n.I18nResourceManager;
import cn.yto.print.constants.AssistantConstants;
import cn.yto.print.controller.IndexController;
import cn.yto.print.enums.MessageTypeEnum;
import cn.yto.print.enums.PluginRegistryEnum;
import cn.yto.print.exception.PluginCommException;
import cn.yto.print.service.handle.ITextMessageFunction;
import cn.yto.print.socket.PrintSocketEndpoint;
import cn.yto.print.system.SpringContextHolder;
import cn.yto.print.utils.PluginVerisonUtil;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

public class SocketSessionContext {

    private static final Logger logger = LoggerFactory.getLogger(SocketSessionContext.class);

    /**
     * 最大重试次数
     */
    private static final int MAX_RETRIES = 3;
    private static final String PLUGIN_SERVER_IP_PREFIX = "ws://127.0.0.1:";

    private final SocketConnectionManager socketManager;
    private final AtomicReference<WebSocketSession> webSocketSessionRef = new AtomicReference<>();
    private final AtomicBoolean connectedFlag = new AtomicBoolean(false);
    private final AtomicReference<String> platformNameRef = new AtomicReference<>();

    public SocketSessionContext(PluginRegistryEnum printSocketEnum, ITextMessageFunction textHandler) {
        initPlatformName(printSocketEnum);
        StandardWebSocketClient client = createWebSocketClient();
        // 创建连接管理器
        socketManager = buildSocketManager(client, printSocketEnum, textHandler);
        socketManager.start();
    }

    private void initPlatformName(PluginRegistryEnum enumVal) {
        this.platformNameRef.set(enumVal.getName());
    }

    private StandardWebSocketClient createWebSocketClient() {
        return new StandardWebSocketClient();
    }

    /**
     * 创建并配置 SocketConnectionManager
     */
    private SocketConnectionManager buildSocketManager(StandardWebSocketClient client,
                                                       PluginRegistryEnum registryEnum,
                                                       ITextMessageFunction textHandler) {
        return new SocketConnectionManager(client, new TextWebSocketHandler() {
            @Override
            public void afterConnectionEstablished(@NotNull WebSocketSession session) {
                handleAfterConnected(session, registryEnum);
            }

            @Override
            protected void handleTextMessage(@NotNull WebSocketSession session, @NotNull TextMessage message) {
                processTextMessage(session, message, registryEnum, textHandler);
            }

            @Override
            public void afterConnectionClosed(@NotNull WebSocketSession session, CloseStatus status) {
                handleAfterClosed(session, status, registryEnum);
            }

            @Override
            public void handleTransportError(@NotNull WebSocketSession session, @NotNull Throwable exception) {
                logger.error("【{}】打印组件数据传输异常", registryEnum.getName(), exception);
            }
        },
                PLUGIN_SERVER_IP_PREFIX + registryEnum.getPortUrl(), registryEnum);
    }

    private void handleAfterConnected(WebSocketSession session, PluginRegistryEnum registryEnum) {
        connectedFlag.set(true);
        webSocketSessionRef.set(session);
        IndexController.setPluginStatusChange(registryEnum, AssistantConstants.OPENED);
        PluginVerisonUtil.sendAgentInfo(session, registryEnum);
        PrintSocketEndpoint.handlePluginStatusAll();
        String message = String.format(
                I18nResourceManager.getInstance().get("index.console.build"),
                I18nResourceManager.getInstance().get(registryEnum.getLocale())
        );
        SpringContextHolder.getBean(IndexController.class).addConsoleLog(message, MessageTypeEnum.SUCCESS);
        logger.info("【{}】打印组件连接通道已建立", registryEnum.getName());
    }

    private void processTextMessage(WebSocketSession session, TextMessage msg,
                                    PluginRegistryEnum registryEnum, ITextMessageFunction handler) {
        logger.info("收到【{}】打印组件回执消息：{}", registryEnum.getName(), msg.getPayload());
        try {
            handler.handleMessageReceived(session, msg);
        } catch (Exception e) {
            logger.error("处理【{}】打印组件回执消息异常", registryEnum.getName(), e);
        }
    }

    /**
     * 连接关闭
     */
    private void handleAfterClosed(WebSocketSession session, CloseStatus status, PluginRegistryEnum registryEnum) {
        SpringContextHolder.getBean(IndexController.class).addConsoleLog(
                String.format(
                        I18nResourceManager.getInstance().get("index.console.closed"),
                        I18nResourceManager.getInstance().get(registryEnum.getLocale())
                ),
                MessageTypeEnum.ERROR
        );
        logger.info("【{}】打印组件连接通道关闭：{}", registryEnum.getName(), status);
        connectedFlag.set(false);
        webSocketSessionRef.set(null);
        IndexController.setPluginStatusChange(registryEnum, AssistantConstants.CLOSED);
        reConnect();
        PrintSocketEndpoint.handlePluginStatusAll();
    }

    public String getPlatformNameRef() {
        return platformNameRef.get();
    }

    public WebSocketSession getSession() {
        return webSocketSessionRef.get();
    }

    /**
     * 重连逻辑
     */
    private boolean reConnect() {
        for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
            String message = String.format(
                    I18nResourceManager.getInstance().get("index.console.reConnect"),
                    attempt, MAX_RETRIES
            );
            SpringContextHolder.getBean(IndexController.class).addConsoleLog(message, MessageTypeEnum.WARNING);
            logger.info("正在重试 {}/{} 次连接【{}】打印组件 ...", attempt, MAX_RETRIES, getPlatformNameRef());
            try {
                socketManager.stop();
                socketManager.start();
                Thread.sleep(1000L * attempt);
                if (connectedFlag.get()) {
                    return true;
                }
            } catch (Exception ignored) {

            }
        }
        return false;
    }

    /**
     * 发送打印任务
     */
    public void sendPrintRequest(Object printParams) {
        WebSocketSession session = webSocketSessionRef.get();
        if (session != null && session.isOpen()) {
            sendJsonMessage(session, printParams);
        } else {
            if (reConnect()) {
                logger.info("【{}】打印组件通道重新建立，再次发送打印任务", getPlatformNameRef());
                sendPrintRequest(printParams);
            } else {
                logger.error("【{}】打印组件通道重新建立异常", getPlatformNameRef());
                throw new PluginCommException("打印组件连接异常");
            }
        }
    }

    private void sendJsonMessage(WebSocketSession session, Object obj) {
        try {
            String jsonStr = JSONUtil.toJsonStr(obj);
            logger.info("发送指令给【{}】打印组件：{}", getPlatformNameRef(), jsonStr);
            session.sendMessage(new TextMessage(jsonStr));
        } catch (Exception e) {
            logger.error("发送指令给【{}】打印组件异常", getPlatformNameRef(), e);
            throw new PluginCommException("发送组件指令异常", e);
        }
    }

}

